package com.nulldev.util.web.HttpServer3.extensions.nCaptcha.captchas.auto.math;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.RandomUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.data.Variables;

public class MathChallengeGenerator {

	private static final int DEFAULT_AMOUNT = 16;
	private static final boolean MULTIPLICATION_USE_IMUL = Arguments.hasArgument("--nCaptcha.useIMUL", "--MathChallengeGenerator.useIMUL");

	public static class MathChallengeContext {
		private final int initialValue;
		private final double expectedValue;
		private final String payload;

		private MathChallengeContext(final String payload, final int initialValue, final double expectedValue) {
			this.payload = payload;
			this.initialValue = initialValue;
			this.expectedValue = expectedValue;
		}

		public int getInitialValue() {
			return this.initialValue;
		}

		public double getExpectedValue() {
			return this.expectedValue;
		}

		public String getPayload() {
			return this.payload;
		}
	}

	public static MathChallengeContext generate() {
		final MathChallengeSegmentType[] segments = generateSegments(DEFAULT_AMOUNT);
		final int initialValue = RandomUtil.randomIntPositive();
		double expectedValue = initialValue;
		final String variableName = StringsUtil.randomStringAlphabet(3);
		final StringBuilder builder = new StringBuilder(128);
		builder.append(variableName + "=" + initialValue + ";");
		for (final MathChallengeSegmentType segment : segments) {
			switch (segment) {
				case ADDITION: {
					final double rand = RandomUtil.randomIntPositive();
					expectedValue = (expectedValue + rand);
					builder.append(variableName + "=(" + variableName + "+" + rand + ");");
					break;
				}
				case ADDITION_ASSIGNMENT: {
					final double rand = RandomUtil.randomIntPositive();
					expectedValue = (expectedValue + rand);
					builder.append(variableName + "+=" + rand + ";");
					break;
				}
				case SUBTRACTION: {
					final double rand = RandomUtil.randomIntPositive();
					expectedValue = (expectedValue - rand);
					builder.append(variableName + "=(" + variableName + "-" + rand + ");");
					break;
				}
				case SUBTRACTION_ASSIGNMENT: {
					final double rand = RandomUtil.randomIntPositive();
					expectedValue -= rand;
					builder.append(variableName + "-=" + rand + ";");
					break;
				}
				case MULTIPLICATION: {
					final double rand = RandomUtil.randInt(2, 6);
					expectedValue = (expectedValue * rand);
					if (MULTIPLICATION_USE_IMUL) {
						builder.append(variableName + "=Math.imul(" + variableName + "," + rand + ");");
					} else {
						builder.append(variableName + "=(" + variableName + "*" + rand + ");");
					}
					break;
				}
				case MULTIPLICATION_ASSIGNMENT: {
					final double rand = RandomUtil.randInt(2, 6);
					expectedValue *= rand;
					builder.append(variableName + "*=" + rand + ";");
					break;
				}
				case INCREMENT: {
					expectedValue++;
					builder.append(variableName + "++;");
					break;
				}
				case DECREMENT: {
					expectedValue--;
					builder.append(variableName + "--;");
					break;
				}
				case DIVISION: {
					final double rand = RandomUtil.randInt(2, 6);
					expectedValue /= rand;
					builder.append(variableName + "=(" + variableName + "/" + rand + ");");
					break;
				}
				case DIVISION_ASSIGNMENT: {
					final double rand = RandomUtil.randInt(2, 6);
					expectedValue /= rand;
					builder.append(variableName + "/=" + rand + ";");
					break;
				}
				case EXPONENT: {
					final double rand = RandomUtil.randInt(1, 3);
					expectedValue = Math.pow(expectedValue, rand);
					builder.append(variableName + "=Math.pow(" + variableName + "," + rand + ");");
					break;
				}
				case MODULUS: {
					final double rand = RandomUtil.randomIntPositive();
					expectedValue %= rand;
					builder.append(variableName + "%=" + rand + ";");
					break;
				}
				default:
					throw new UnsupportedOperationException("Unknown segment type: " + segment);
			}
		}
		builder.append(variableName + ";");
		return new MathChallengeContext(builder.toString(), initialValue, expectedValue);
	}

	private static MathChallengeSegmentType[] generateSegments(final int amount) {
		Variables.checkArgument(amount > 0);
		final MathChallengeSegmentType[] seg = new MathChallengeSegmentType[amount];
		for (int i = 0; i < amount; i++)
			seg[i] = ArrayUtils.random(MathChallengeSegmentType.values());
		return seg;
	}

	public static void main(String[] args) {
		final MathChallengeContext ctx = generate();
		JVM.println("initialValue=" + ctx.getInitialValue() + ", expectedValue=" + ctx.getExpectedValue());
		JVM.println(ctx.getPayload());
	}
}
